#include "stdafx.h"
#include "DialogEx.h"
#include "resource.h"

#pragma comment(lib, "Msimg32.lib")

/////////////////////////////////////////////////////////////////////////////
// CDialogEx dialog

BEGIN_MESSAGE_MAP(CDialogEx, CDialog)
	ON_WM_NCPAINT()
	ON_WM_NCACTIVATE()
END_MESSAGE_MAP()

CDialogEx::CDialogEx (
		UINT nTemplate, 
		UINT uId_Icon, 
		CWnd* pParent, /* =NULL */
		COLORREF crCaption_Active_Left, /* =0x00808000 | I_OS_CAL */
		COLORREF crCaption_Active_Right, /* =0x00800080 | I_OS_CAR */
		COLORREF crCaption_Inactive_Left, /* =I_OS_CIL */
		COLORREF crCaption_Inactive_Right /* =I_OS_CIR */ ) :
	m_crCaption_Active_Left(crCaption_Active_Left), 
	m_crCaption_Active_Right(crCaption_Active_Right), 
	m_crCaption_Inactive_Left(crCaption_Inactive_Left), 
	m_crCaption_Inactive_Right(crCaption_Inactive_Right), 
	CDialog(nTemplate, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(uId_Icon);
}

CDialogEx::~CDialogEx()
{
}

BOOL CDialogEx::OnInitDialog()
{
	CDialog::OnInitDialog();

	SetIcon(m_hIcon, TRUE);
	SetIcon(m_hIcon, FALSE);

	return TRUE;
}

BOOL CDialogEx::OnNcActivate(BOOL bActive)
{
	// Mimic MFC kludge to stay active if WF_STAYACTIVE bit is on
	//
	if (m_nFlags & WF_STAYACTIVE)
		bActive = TRUE;
	if (!IsWindowEnabled())			// but not if disabled
		bActive = FALSE;

	if (bActive==m_bActive)
		return TRUE;					// nothing to do

	// Turn WS_VISIBLE off before callinf DefWindowProc,
	// so DefWindowProc won't paint and thereby cause flicker.
	//
	DWORD dwStyle = GetStyle();
	if (dwStyle & WS_VISIBLE)
		SetWindowLong(m_hWnd, GWL_STYLE, (dwStyle & ~ WS_VISIBLE));
	// do not call the base class because it will call Default()
	// and we may have changed bActive.
	DefWindowProc(WM_NCACTIVATE, bActive, 0L);
	if (dwStyle & WS_VISIBLE)
		SetWindowLong(m_hWnd, GWL_STYLE, dwStyle);

	// At this point, nothing has happened (since WS_VISIBLE was off).
	// Now it's time to paint.
	//
	m_bActive = bActive;			// update state
	SendMessage(WM_NCPAINT);		// paint non-client area (frame too)
	return true;	
}

void CDialogEx::OnNcPaint()
{
	MSG& msg = AfxGetThreadState()->m_lastSentMsg;
	HRGN hRgn = (HRGN)msg.wParam;

	CCaptionRect rc(*this);		// caption rectangle in window coords
	CRect rcWin;				// window rect
	GetWindowRect(&rcWin);		// .. get window rect
	rc += rcWin.TopLeft();		// convert caption rect to screen coords

	// Don't bother painting if the caption doesn't lie within the region.
	//
	if (msg.wParam > 1 && !::RectInRegion((HRGN)msg.wParam, &rc)) 
	{
		CDialog::OnNcPaint(); // just do default thing
		return;					// and quit
	}

	// Exclude caption from update region
	//
	HRGN hRgnCaption = ::CreateRectRgnIndirect(&rc);
	HRGN hRgnNew     = ::CreateRectRgnIndirect(&rc);
	if (msg.wParam > 1) 
	{
		// wParam is a valid region: subtract caption from it
		::CombineRgn(hRgnNew, hRgn, hRgnCaption, RGN_DIFF);
	} else 
	{
		// wParam is not a valid region: create one that's the whole
		// window minus the caption bar
		HRGN hRgnAll = ::CreateRectRgnIndirect(&rcWin);
		CombineRgn(hRgnNew, hRgnAll, hRgnCaption, RGN_DIFF);
		DeleteObject(hRgnAll);
	}

	// Call Windows to do WM_NCPAINT with altered update region
	//
	WPARAM savewp = msg.wParam;		// save original wParam
	msg.wParam = (WPARAM)hRgnNew;	// set new region for DefWindowProc
	CDialog::OnNcPaint();			// call it
	DeleteObject(hRgnCaption);		// clean up
	DeleteObject(hRgnNew);			// ...
	msg.wParam = savewp;			// restore original wParam

	PaintCaption();					// Now paint my special caption
}

void CDialogEx::OnPaint()
{
	if(IsIconic())
	{
		CPaintDC dc(this);
		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
		int ix = ::GetSystemMetrics(SM_CXICON);
		int iy = ::GetSystemMetrics(SM_CYICON);
		CRect irClient;
		CWnd::GetClientRect(&irClient);
		int ix_pos = (irClient.Width() - ix + 1) / 2;
		int iy_pos = (irClient.Height() - iy + 1) / 2;
		dc.DrawIcon(ix_pos, iy_pos, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

HCURSOR CDialogEx::OnQueryDragIcon()
{
	return (HCURSOR)m_hIcon;
}

//////////////////
// Helper to paint rectangle with a color.
//
static void PaintRect(CDC& dc, int x, int y, int w, int h, COLORREF color)
{
	CBrush brush(color);
	CBrush* pOldBrush = dc.SelectObject(&brush);
	dc.PatBlt(x, y, w, h, PATCOPY);
	dc.SelectObject(pOldBrush);
}

//////////////////
// Get the appropriate bitmap whether active/inactive. If the size of
// the caption rect has changed since the last time it was generated,
// generate it again.
// 
// This is the function that actually does the shading. It creates a
// bitmap that's used to paint the caption. It looks horrible, but it's
// just a lot of bit-twiddling GDI stuff.
//
CBitmap* CDialogEx::GetCaptionBitmap(BOOL bActive)
{
	CBitmap& bm = m_bmCaption[bActive!=0];	// one of two bitmaps
	CCaptionRect rcCap(*this);				// caption rectangle in win coords
	if (rcCap != m_rcCaption) {				// if changed since last time:
		m_bmCaption[0].DeleteObject();		// delete both bitmaps,
		m_bmCaption[1].DeleteObject();		// ..they are bad
		m_rcCaption = rcCap;				// note for next time
	}
	if (bm.m_hObject)
		return &bm;							// already have bitmap; return it.
	
	// Either the size changed or the bitmap was never created. Create it.
	// 
	rcCap -= rcCap.TopLeft(); // convert caption rectangle origin to (0,0)
	int w = rcCap.Width();
	int h = rcCap.Height();
	int cxIcon = GetSystemMetrics(SM_CXSIZE);
	int cyIcon = GetSystemMetrics(SM_CYSIZE);

	// Create bitmap same size as caption area and select into memory DC
	//
	CWindowDC dcWin(this);
	CDC dc;
	dc.CreateCompatibleDC(&dcWin);
	bm.DeleteObject();
	bm.CreateCompatibleBitmap(&dcWin, w, h);
	CBitmap* pOldBitmap = dc.SelectObject(&bm);

	// Paint shaded background. Note all painting is into memory DC,
	// not window, so there's no flicker.
	//
	TRIVERTEX vert[2];
	GRADIENT_RECT gRect;
	
	// Gradient fill.
	CONST COLORREF crCaptionLeft = bActive ? m_crCaption_Active_Left : m_crCaption_Inactive_Left,
				   crCaptionRight = bActive ? m_crCaption_Active_Right : m_crCaption_Inactive_Right;

	vert [0] .x = rcCap.left;
	vert [0] .y = rcCap.top;
	vert [0] .Red = GetRValue(crCaptionLeft) * 0x100;
	vert [0] .Green = GetGValue(crCaptionLeft) * 0x100;
	vert [0] .Blue = GetBValue(crCaptionLeft) * 0x100;
	vert [0] .Alpha = 0x0000;
	vert [1] .x = rcCap.right-rcCap.left;
	vert [1] .y = rcCap.bottom-rcCap.top; 
	vert [1] .Red = GetRValue(crCaptionRight) * 0x100;
	vert [1] .Green = GetGValue(crCaptionRight) * 0x100;
	vert [1] .Blue = GetBValue(crCaptionRight) * 0x100;
	vert [1] .Alpha = 0x0000;
	
	gRect.UpperLeft = 0;
	gRect.LowerRight = 1; 
	
	// fill the area 
	::GradientFill( dc.m_hDC, vert, 2, &gRect, 1, GRADIENT_FILL_RECT_H );

/*	typedef BOOL (CALLBACK* FUN)(HDC,PTRIVERTEX,ULONG,PVOID,ULONG,ULONG);
	HINSTANCE dllHandle = NULL;
	FUN fun = NULL;
	//Load the dll and keep the handle to it
	dllHandle = ::LoadLibrary("msimg32.dll");
	if (NULL != dllHandle)
	{
		fun = (FUN)GetProcAddress(dllHandle, "GradientFill");
		if (fun != NULL)
			fun( dc.m_hDC, vert, 2, &gRect, 1, GRADIENT_FILL_RECT_H );
		::FreeLibrary(dllHandle);
	}
*/

	DWORD dwStyle = GetStyle();
	CRect rcButn(0,0,cxIcon,cyIcon);

	DWORD bSysMenu = (dwStyle & WS_SYSMENU);
	if (bSysMenu)
	{
		// Draw icon and caption buttons.
		// These are all drawn inside a rectangle of dimensions SM_C[XY]SIZE.

		// Within the basic button rectangle, Windows 95 uses a 1 or 2 pixel border
		// Icon has 2 pixel border on left, 1 pixel on top/bottom, 0 right
		rcButn.DeflateRect(0,1);
		rcButn.left += 2;
		DrawIconEx(dc.m_hDC, rcButn.left, rcButn.top, m_hIcon,
			rcButn.Width(), rcButn.Height(), 0, NULL, DI_NORMAL);

		// Close box has a 2 pixel border on all sides but left, which is zero
		rcButn.DeflateRect(0,1);			// shrink top/bottom 1 more
		rcButn += CPoint(w-cxIcon-2,0);		// move right
		dc.DrawFrameControl(&rcButn, DFC_CAPTION, DFCS_CAPTIONCLOSE);

		// Max/restore button is like close box; just shift rectangle left
		//
		if (dwStyle & WS_MAXIMIZEBOX) {
			rcButn -= CPoint(cxIcon,0);
			dc.DrawFrameControl(&rcButn, DFC_CAPTION,
				IsZoomed() ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX);
		}
		else if (dwStyle & WS_MINIMIZEBOX) {
			rcButn -= CPoint(cxIcon,0);
			dc.DrawFrameControl(&rcButn, DFC_CAPTION,
				IsZoomed() ? DFCS_CAPTIONRESTORE|DFCS_INACTIVE : DFCS_CAPTIONMAX|DFCS_INACTIVE );
		}

		// Minimize button has 2 pixel border on all sides but right.
		//
		if (dwStyle & WS_MINIMIZEBOX) {
			rcButn -= CPoint(cxIcon-2,0);
			dc.DrawFrameControl(&rcButn, DFC_CAPTION, DFCS_CAPTIONMIN);
		}
		else if (dwStyle & WS_MAXIMIZEBOX) {
			rcButn -= CPoint(cxIcon-2,0);
			dc.DrawFrameControl(&rcButn, DFC_CAPTION, DFCS_CAPTIONMIN|DFCS_INACTIVE);
		}
	}
	// Now draw text. First Create fonts if needed
	//
	if (!m_fontCaption.m_hObject)
		CreateFonts();

	CString sTitle(_T(""));						// app title
	GetWindowText(sTitle);
	CRect rcText = rcCap;						// caption rectangle
	rcText.left += bSysMenu ? (cxIcon+2) : 2;	// start after icon
	rcText.right = bSysMenu ? (rcButn.left-2) : (rcText.right-2);	// don't draw past buttons

	COLORREF clrText;							// text color
	if (bActive)
		clrText = GetSysColor(COLOR_CAPTIONTEXT);
	else
		clrText = GetSysColor(COLOR_INACTIVECAPTIONTEXT);

	dc.SetBkMode(TRANSPARENT);				// draw on top of our shading
	dc.SetTextColor(clrText);
	CFont* pOldFont = dc.SelectObject(&m_fontCaption);
	dc.DrawText(sTitle, &rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS);

	// Restore DC
	dc.SelectObject(pOldFont);
	dc.SelectObject(pOldBitmap);

	return &bm; // return bitmap to caller--whew!
}

//////////////////
// Paint custom caption. Flag tells whether active or not.
// Just blast the bitmap to the title bar, but not iif minimized (iconic).
//
void CDialogEx::PaintCaption(BOOL bActive)
{
	if (!IsIconic()) {
		CWindowDC dcWin(this);
		CDC dc;
		dc.CreateCompatibleDC(&dcWin);
		CBitmap* pOldBitmap = dc.SelectObject(GetCaptionBitmap(bActive));
		const CRect& rc = m_rcCaption;
		dcWin.BitBlt(rc.left,rc.top,rc.Width(),rc.Height(),&dc,0,0,SRCCOPY);
	}
}

//////////////////
// Helper function to build the fonts I need.
//
void CDialogEx::CreateFonts()
{
	// Get current system caption font, just to get its size
	//
	NONCLIENTMETRICS ncm;
	ncm.cbSize = sizeof(ncm);
	VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0));
	m_fontCaption.CreateFontIndirect(&ncm.lfCaptionFont);
}

//////////////////
// CCaptionRect: Constructor conputes caption rectangle in window coords.
//
CCaptionRect::CCaptionRect(const CWnd& wnd)
{
	// Get size of frame around window
	DWORD dwStyle = wnd.GetStyle();
	CSize szFrame = (dwStyle & WS_THICKFRAME) ?
		CSize(GetSystemMetrics(SM_CXSIZEFRAME),
			   GetSystemMetrics(SM_CYSIZEFRAME)) :
		CSize(GetSystemMetrics(SM_CXFIXEDFRAME),
				GetSystemMetrics(SM_CYFIXEDFRAME));

	int cxIcon = GetSystemMetrics(SM_CXSIZE); // width of caption icon/button

	// Compute rectangle
	wnd.GetWindowRect(this);		// window rect in screen coords
	*this -= CPoint(left, top);		// shift origin to (0,0)
	left  += szFrame.cx;			// frame
	right -= szFrame.cx;			// frame
	top   += szFrame.cy;			// top = end of frame
	bottom = top + GetSystemMetrics(SM_CYCAPTION)  // height of caption
		- GetSystemMetrics(SM_CYBORDER);				  // minus gray shadow border
}
